import Foundation
import DCloudUTSFoundation

// struct MergeOptions {
//     var arrayMerge: ([Any], [Any], MergeOptions) -> [Any] = defaultArrayMerge
//     var clone: Bool = true
//     var isMergeableObject: (Any) -> Bool = { value in
//         value is [String: Any] || value is [Any]
//     }
// }

func _merge<T>(_ target: T, _ source: Any, options: MergeOptions = MergeOptions()) -> T {
	switch (target, source) {
	case (let targetDict as [String: Any], let sourceDict as [String: Any]):
		var merged = cloneValue(targetDict, options: options) as! [String: Any]
		for (key, sourceVal) in sourceDict {
			let clonedSource = cloneValue(sourceVal, options: options)
			guard let existingVal = merged[key] else {
				merged[key] = clonedSource
				continue
			}
			
			// 处理可合并对象
			if options.isMergeableObject(existingVal) && options.isMergeableObject(clonedSource) {
				switch (existingVal, clonedSource) {
				case (let existingDict as [String: Any], let sourceDict as [String: Any]):
					merged[key] = merge(existingDict, sourceDict, options: options)
				case (let existingArr as [Any], let sourceArr as [Any]):
					merged[key] = options.arrayMerge(existingArr, sourceArr, options: options)
				default:
					merged[key] = clonedSource
				}
			} else {
				merged[key] = clonedSource
			}
		}
		return merged as! T
	case (let targetArray as [Any], let sourceArray as [Any]):
		let clonedTarget = targetArray.map { cloneValue($0, options: options) }
		let clonedSource = sourceArray.map { cloneValue($0, options: options) }
		return options.arrayMerge(clonedTarget, clonedSource, options: options) as! T
	default:
		return cloneValue(source, options: options) as! T
	}
}

func _mergeAll<T>(_ objects: [Any], options: MergeOptions = MergeOptions()) -> T {
	guard !objects.isEmpty else { fatalError("objects is empty") }
	guard let first = objects.first else { fatalError("objects is empty") }
	
	return objects.dropFirst().reduce(cloneValue(first, options: options)) { result, next in
		_merge(result, next, options: options)
	} as! T
}

private func defaultArrayMerge(target: [Any], source: [Any], options: MergeOptions) -> [Any] {
	return target.map { cloneValue($0, options: options) } + source.map { cloneValue($0, options: options) }
}

private func cloneValue(_ value: Any, options: MergeOptions) -> Any {
	guard options.clone else { return value }
	    
	switch value {
	case let dict as [String: Any]:
		return dict.mapValues { cloneValue($0, options: options) }
	case let array as [Any]:
		return array.map { cloneValue($0, options: options) }
	default:
		return value
	}
}